#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/i2c.h>
#include <linux/ipmi_smi.h>
#include <linux/init.h>
#include <linux/dmi.h>
#include <linux/kthread.h>
#include <linux/acpi.h>
#include <linux/ctype.h>
#include <linux/time64.h>
#include <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/i2c.h>

#define IOC_AT24C02_READ 100
#define IOC_AT24C02_WRITE 101
#define IOC_AT24C02_READ_ALL 102
static int major;
static struct class *at24c02_class;
static struct device *at24c02_device;
static struct i2c_client *at24c02_client;
static long at24c02_ioctl(struct file *fl, unsigned int cmd, unsigned long arg)
{
    int i;
    struct i2c_msg msgs[2];
    unsigned int kerbuf[257];
    unsigned char kerbuf_all[257];
    unsigned char addr;
    unsigned int *usr_buf = (unsigned int *)arg;
    copy_from_user(kerbuf, usr_buf, 8);
    unsigned char byte_buf[2];
    addr = kerbuf[0];
    switch (cmd)
    {
    case IOC_AT24C02_READ:
        msgs[0].addr = at24c02_client->addr;
        msgs[0].buf = (unsigned char *)&kerbuf[0];
        msgs[0].flags = 0;
        msgs[0].len = 1;

        msgs[1].addr = at24c02_client->addr;
        msgs[1].buf = (unsigned char *)&kerbuf[1];
        msgs[1].flags = 1;
        msgs[1].len = 1;

        i2c_transfer(at24c02_client->adapter, msgs, 2);
        printk(" read addr 0x%x, data 0x%x \n", kerbuf[0], kerbuf[1]);
        copy_to_user(usr_buf, kerbuf, 8);

        break;

    case IOC_AT24C02_WRITE:
        byte_buf[0] = addr;
        byte_buf[1] = kerbuf[1];
        printk(" write addr 0x%x, data 0x%x \n", kerbuf[0], kerbuf[1]);
        printk("sizeof data %d \n", sizeof(kerbuf));
        msgs[0].addr = at24c02_client->addr;
        msgs[0].buf = byte_buf;
        msgs[0].flags = 0;
        msgs[0].len = 2;
        i2c_transfer(at24c02_client->adapter, msgs, 1);
        mdelay(20);
        break;
    case IOC_AT24C02_READ_ALL:
        kerbuf_all[0] = 0x0;
        msgs[0].addr = at24c02_client->addr;
        msgs[0].buf = (unsigned char *)&kerbuf_all[0];
        msgs[0].flags = 0;
        msgs[0].len = 1;
        // for (i = 0; i < 256; i++)
        // {
        //     msgs[i+1].addr = at24c02_client->addr;
        //     msgs[i+1].buf = (unsigned char *)&kerbuf[1+i];
        //     msgs[i+1].flags = 1;
        //     msgs[i+1].len = 1;
        // }
        msgs[1].addr = at24c02_client->addr;
        msgs[1].buf = (unsigned char *)&kerbuf_all[1];
        msgs[1].flags = 1;
        msgs[1].len = 256;

        i2c_transfer(at24c02_client->adapter, msgs, 2);
        // printk(" read addr 0x%x, data 0x%x \n", kerbuf[0], kerbuf[1]);
        copy_to_user(usr_buf, kerbuf_all, 257);
        break;
    }

    return 0;
}
static struct file_operations at24c02_opr = {
    .unlocked_ioctl = at24c02_ioctl,
    .owner = THIS_MODULE,
};

int at24c02_probe(struct i2c_client *client, const struct i2c_device_id *dev_id)
{
    // struct i2c_adapter
    at24c02_client = client;
    major = register_chrdev(0, "at24c02", &at24c02_opr);

    at24c02_class = class_create(THIS_MODULE, "at24c02");
    if (IS_ERR(at24c02_class))
    {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        unregister_chrdev(major, "at24c02");
        return PTR_ERR(at24c02_class);
    }
    at24c02_device = device_create(at24c02_class, NULL, MKDEV(major, 0), NULL, "chen_at24c02");

    return 0;
}
int at24c02_remove(struct i2c_client *client)
{
    printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

    device_destroy(at24c02_class, MKDEV(major, 0));
    class_destroy(at24c02_class);
    unregister_chrdev(major, "at24c02");

    return 0;
}
static const struct i2c_device_id at24c02_ids[] = {
    {"xxxxyyy", (kernel_ulong_t)NULL},
    {}};
struct of_device_id at24c02_table[] = {
    {.compatible = "chen_at24c02"}, {}};
static struct i2c_driver at24c02_drv = {
    .driver = {
        .name = "at24c02",
        .of_match_table = at24c02_table,

    },
    .id_table = at24c02_ids,
    .probe = at24c02_probe,
    .remove = at24c02_remove};
static int __init at24c02_init(void)
{
    i2c_add_driver(&at24c02_drv);
    return 0;
}
static void at24c02_exit(void)
{
    i2c_del_driver(&at24c02_drv);
}

module_init(at24c02_init);
module_exit(at24c02_exit);
MODULE_LICENSE("GPL");